{
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "he subset of available tools to call is generally at the discretion of the model (although many providers also enable the user to specify or constrain the choice of tool). As the number of available tools grows, you may want to limit the scope of the LLM's selection, to decrease token consumption and to help manage sources of error in LLM reasoning.\n",
    "\n",
    "Here we will demonstrate how to dynamically adjust the tools available to a model. Bottom line up front: like RAG and similar methods, we prefix the model invocation by retrieving over available tools. Although we demonstrate one implementation that searches over tool descriptions, the details of the tool selection can be customized as needed.\n",
    "\n",
    "## Define the tools\n",
    "Let's consider a toy example in which we have one tool for each publicly traded company in the [S&P 500 index](https://en.wikipedia.org/wiki/S%26P_500). Each tool fetches company-specific information based on the year provided as a parameter.\n",
    "\n",
    "We first construct a registry that associates a unique identifier with a schema for each tool. We will represent the tools using JSON schema, which can be bound directly to chat models supporting tool calling."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "b92a88c6f118d749"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "import re\n",
    "import uuid\n",
    "\n",
    "from langchain_core.tools import StructuredTool\n",
    "\n",
    "\n",
    "def create_tool(company: str) -> StructuredTool:\n",
    "    \"\"\"Create schema for a placeholder tool.\"\"\"\n",
    "    # Remove non-alphanumeric characters and replace spaces with underscores for the tool name\n",
    "    formatted_company = re.sub(r\"[^\\w\\s]\", \"\", company).replace(\" \", \"_\")\n",
    "\n",
    "    def company_tool(year: int) -> str:\n",
    "        # Placeholder function returning static revenue information for the company and year\n",
    "        return f\"{company} had revenues of $100 in {year}.\"\n",
    "\n",
    "    return StructuredTool.from_function(\n",
    "        company_tool,\n",
    "        name=formatted_company,\n",
    "        description=f\"Information about {company}\",\n",
    "    )\n",
    "\n",
    "# Abbreviated list of S&P 500 companies for demonstration\n",
    "s_and_p_500_companies = [\n",
    "    \"3M\",\n",
    "    \"A.O. Smith\",\n",
    "    \"Abbott\",\n",
    "    \"Accenture\",\n",
    "    \"Advanced Micro Devices\",\n",
    "    \"Yum! Brands\",\n",
    "    \"Zebra Technologies\",\n",
    "    \"Zimmer Biomet\",\n",
    "    \"Zoetis\",\n",
    "]\n",
    "\n",
    "# Create a tool for each company and store it in a registry with a unique UUID as the key\n",
    "tool_registry = {\n",
    "    str(uuid.uuid4()): create_tool(company) for company in s_and_p_500_companies\n",
    "}"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T03:37:15.338266Z",
     "start_time": "2024-11-05T03:37:15.291893Z"
    }
   },
   "id": "de7e08e08d5eb407",
   "execution_count": 2
  },
  {
   "cell_type": "markdown",
   "source": [
    "## Define the graph\n",
    "### Tool selection\n",
    "We will construct a node that retrieves a subset of available tools given the information in the state-- such as a recent user message. In general, the full scope of retrieval solutions are available for this step. As a simple solution, we index embeddings of tool descriptions in a vector store, and associate user queries to tools via semantic search."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "f432ae07c34b2e18"
  },
  {
   "cell_type": "code",
   "outputs": [],
   "source": [
    "import os\n",
    "from langchain_community.embeddings import DashScopeEmbeddings\n",
    "from langchain_core.documents import Document\n",
    "from langchain_core.vectorstores import InMemoryVectorStore\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "load_dotenv()\n",
    "tool_documents = [\n",
    "    Document(\n",
    "        page_content=tool.description,\n",
    "        id=id,\n",
    "        metadata={\"tool_name\": tool.name},\n",
    "    )\n",
    "    for id, tool in tool_registry.items()\n",
    "]\n",
    "\n",
    "vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(\n",
    "    dashscope_api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "))\n",
    "document_ids = vector_store.add_documents(tool_documents)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T03:37:16.116012Z",
     "start_time": "2024-11-05T03:37:15.340949Z"
    }
   },
   "id": "a6d9d62e8d743df4",
   "execution_count": 3
  },
  {
   "cell_type": "markdown",
   "source": [
    "### Incorporating with an agent\n",
    "We will use a typical React agent graph (e.g., as used in the [quickstart](https://langchain-ai.github.io/langgraph/tutorials/introduction/#part-2-enhancing-the-chatbot-with-tools)), with some modifications:\n",
    "\n",
    "- We add a `selected_tools` key to the state, which stores our selected subset of tools;\n",
    "- We set the entry point of the graph to be a `select_tools` node, which populates this element of the state;\n",
    "- We bind the selected subset of tools to the chat model within the `agent` node."
   ],
   "metadata": {
    "collapsed": false
   },
   "id": "3f2841671f724979"
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": "<IPython.core.display.Image object>"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from IPython.core.display import Image\n",
    "from typing import Annotated\n",
    "\n",
    "from langchain_openai import ChatOpenAI\n",
    "from typing_extensions import TypedDict\n",
    "\n",
    "from langgraph.graph import StateGraph, START\n",
    "from langgraph.graph.message import add_messages\n",
    "from langgraph.prebuilt import ToolNode, tools_condition\n",
    "\n",
    "\n",
    "# Define the state structure using TypedDict.\n",
    "# It includes a list of messages (processed by add_messages)\n",
    "# and a list of selected tool IDs.\n",
    "class State(TypedDict):\n",
    "    messages: Annotated[list, add_messages]\n",
    "    selected_tools: list[str]\n",
    "\n",
    "\n",
    "# Retrieve all available tools from the tool registry.\n",
    "tools = list(tool_registry.values())\n",
    "llm = ChatOpenAI(\n",
    "    # 若没有配置环境变量，请用百炼API Key将下行替换为：api_key=\"sk-xxx\",\n",
    "    openai_api_key=os.getenv(\"DASHSCOPE_API_KEY\"),\n",
    "    openai_api_base=\"https://dashscope.aliyuncs.com/compatible-mode/v1\",\n",
    "    model_name=\"qwen-max\",\n",
    "    temperature=0, streaming=True,\n",
    ")\n",
    "\n",
    "# The agent function processes the current state\n",
    "# by binding selected tools to the LLM.\n",
    "def agent(state: State):\n",
    "    # Map tool IDs to actual tools\n",
    "    # based on the state's selected_tools list.\n",
    "    selected_tools = [tool_registry[id] for id in state[\"selected_tools\"]]\n",
    "    # Bind the selected tools to the LLM for the current interaction.\n",
    "    llm_with_tools = llm.bind_tools(selected_tools)\n",
    "    # Invoke the LLM with the current messages and return the updated message list.\n",
    "    return {\"messages\": [llm_with_tools.invoke(state[\"messages\"])]}\n",
    "\n",
    "\n",
    "# The select_tools function selects tools based on the user's last message content.\n",
    "def select_tools(state: State):\n",
    "    last_user_message = state[\"messages\"][-1]\n",
    "    query = last_user_message.content\n",
    "    tool_documents = vector_store.similarity_search(query)\n",
    "    return {\"selected_tools\": [document.id for document in tool_documents]}\n",
    "\n",
    "workflow = StateGraph(State)\n",
    "workflow.add_node(\"agent\", agent)\n",
    "workflow.add_node(\"select_tools\", select_tools)\n",
    "\n",
    "tool_node = ToolNode(tools=tools)\n",
    "workflow.add_node(\"tools\", tool_node)\n",
    "\n",
    "workflow.add_conditional_edges(\"agent\", tools_condition, path_map=[\"tools\", \"__end__\"])\n",
    "workflow.add_edge(\"tools\", \"agent\")\n",
    "workflow.add_edge(\"select_tools\", \"agent\")\n",
    "workflow.add_edge(START, \"select_tools\")\n",
    "graph = workflow.compile()\n",
    "display(Image(graph.get_graph().draw_mermaid_png()))\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T05:11:42.507707Z",
     "start_time": "2024-11-05T05:11:41.107794Z"
    }
   },
   "id": "75eb3a4c6c5644a4",
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['f4248262-ca24-4932-9cfc-5aa41c83bdbe', '5d7bd521-9a21-4386-acbd-eff8bd5e0fb5', '6067dd54-dea1-4b3e-b80b-41dcbd5c07b0', '7145dc8b-f217-4481-99a7-9236e21011c7']\n"
     ]
    }
   ],
   "source": [
    "user_input = \"Can you give me some information about AMD in 2022?\"\n",
    "\n",
    "result = graph.invoke({\"messages\": [(\"user\", user_input)]})\n",
    "print(result[\"selected_tools\"])\n"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T05:12:49.064292Z",
     "start_time": "2024-11-05T05:12:40.660064Z"
    }
   },
   "id": "981a295b9015a7b7",
   "execution_count": 7
  },
  {
   "cell_type": "code",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "================================\u001B[1m Human Message \u001B[0m=================================\n",
      "\n",
      "Can you give me some information about AMD in 2022?\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "Tool Calls:\n",
      "  Advanced_Micro_Devices (call_384aa178433e43bf9d72fc)\n",
      " Call ID: call_384aa178433e43bf9d72fc\n",
      "  Args:\n",
      "    year: 2022\n",
      "=================================\u001B[1m Tool Message \u001B[0m=================================\n",
      "Name: Advanced_Micro_Devices\n",
      "\n",
      "Advanced Micro Devices had revenues of $100 in 2022.\n",
      "==================================\u001B[1m Ai Message \u001B[0m==================================\n",
      "\n",
      "In 2022, Advanced Micro Devices (AMD) reported revenues of $100. Please note that this figure seems to be simplified or illustrative, as AMD's actual revenue would be substantially higher and represented in millions or billions. Would you like more detailed information or context about AMD's performance in 2022?\n"
     ]
    }
   ],
   "source": [
    "for message in result[\"messages\"]:\n",
    "    message.pretty_print()"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-11-05T05:13:14.408026Z",
     "start_time": "2024-11-05T05:13:14.402027Z"
    }
   },
   "id": "bb0b7c6473ec662e",
   "execution_count": 8
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
